package com.li.learning.train.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.li.learning.train.enums.CommitAnswerStatusEnums;
import com.li.learning.train.mapper.TrainMapper;
import com.li.learning.train.mapper.TrainPaperMapper;
import com.li.learning.train.model.dto.CommitTrainDto;
import com.li.learning.train.model.dto.TrainDetailDto;
import com.li.learning.train.model.vo.*;
import com.li.learning.train.service.TrainDetailService;
import com.li.learning.train.service.TrainPaperService;
import com.li.learning.train.service.TrainService;
import com.our.learning.infrastructure.po.Train;
import com.our.learning.infrastructure.po.TrainDetail;
import com.our.learning.infrastructure.po.TrainPaper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class TrainServiceImpl extends ServiceImpl<TrainMapper, Train> implements TrainService{

    @Autowired
    private TrainMapper trainMapper;

    @Autowired
    private TrainPaperMapper trainPaperMapper;

    @Autowired
    private TrainPaperService trainPaperService;

    @Autowired
    private TrainDetailService trainDetailService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TrainVo generateTrain(Integer stuId) {
        //结果
        TrainVo trainVo = new TrainVo();
        List<QuestionInfoVo> voList = new ArrayList<>();
        //生成训练id
        Train train = Train.builder().userId(stuId).build();
        //该方法执行后，对象的id会自动回填
        trainMapper.insert(train);

        //根据当前学生用户id，查询前5次考试的错题中涉及的知识点与章节
        List<KnowledgeAndChapterVo> knowledgeAndChapterVoList = getWrongQuestion(stuId);
        //List<knowledgeAndChapterVo> knowledgeAndChapterVoList = null;
        if (CollUtil.isEmpty(knowledgeAndChapterVoList)) {
            //如果没有错题，那么就随机抽取题目
            voList = generateTrainPaper(null);
        }else {
            //需要抽取的知识点章节以及对应的数量
            List<KnowledgeAndChapterVo> extractVo = new ArrayList<>();
            //只需要错误次数最多的前2个章节
            if (knowledgeAndChapterVoList.size() >= 2) {//因为列表已经排序，err1的数量是大于等于err2的数量
                //如果错题数量超过2个章节，那么就按照错题数量比例生成
                KnowledgeAndChapterVo err1 = knowledgeAndChapterVoList.get(0);
                KnowledgeAndChapterVo err2 = knowledgeAndChapterVoList.get(1);
                //通过BigDecimal进行精确的浮点数计算，实现两个整数相除并保留一位小数
                BigDecimal errNum1 = new BigDecimal(err1.getCountNum());
                BigDecimal errNumSum = new BigDecimal(err1.getCountNum() + err2.getCountNum());
                BigDecimal divide = errNum1.divide(errNumSum, 1, RoundingMode.HALF_UP);
                Integer count1 = divide.multiply(new BigDecimal(10)).intValue();
                extractVo.add(KnowledgeAndChapterVo.builder()
                        .knowledge(err1.getKnowledge())
                        .chapter(err1.getChapter())
                        .countNum(count1)
                        .build());
                if (count1 != 10){
                    extractVo.add(KnowledgeAndChapterVo.builder()
                            .knowledge(err2.getKnowledge())
                            .chapter(err2.getChapter())
                            .countNum(10-count1)
                            .build());
                }
            }else {
                //错题不足2个章节
                extractVo.add(KnowledgeAndChapterVo.builder()
                        .knowledge(knowledgeAndChapterVoList.get(0).getKnowledge())
                        .chapter(knowledgeAndChapterVoList.get(0).getChapter())
                        .countNum(10)
                        .build());
            }
            //根据所得的知识点、章节进行组卷
            voList = generateTrainPaper(extractVo);
        }
        //将组卷结果封装信息并写入数据库
        for (int i = 0;i < voList.size();i++){
            QuestionInfoVo vo = voList.get(i);
            vo.setTrainId(train.getTrainId());
            vo.setTitleNo(i+1);
            TrainPaper trainPaper = BeanUtil.copyProperties(vo, TrainPaper.class);
            trainPaperMapper.insert(trainPaper);
            vo.setQuesInfoId(trainPaper.getQuesInfoId());
        }
        trainVo.setTrainId(train.getTrainId());
        trainVo.setVoList(voList);
        return trainVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo submitTrain(CommitTrainDto dto) {
        Integer trainId = dto.getTrainId();
        List<TrainDetailDto> quesList = dto.getQuesList();
        //从数据库中获取组卷信息
        List<TrainPaper> dbQuesList = trainPaperService.list(
                Wrappers.<TrainPaper>lambdaQuery().eq(TrainPaper::getTrainId, trainId));
        Map<Integer, String> questionIdAndAnswerMap = dbQuesList.stream().collect(Collectors.toMap(TrainPaper::getQuestionId, TrainPaper::getAnswer));
        //将提交的答案与数据库中的答案进行比对
        List<TrainDetail> trainDetailList = new ArrayList<>();
        for (TrainDetailDto detailDto : quesList) {
            TrainDetail trainDetail = BeanUtil.copyProperties(detailDto, TrainDetail.class);
            trainDetail.setTrainId(trainId);
            String answer = questionIdAndAnswerMap.get(detailDto.getQuesInfoId());
            if (StrUtil.isNotEmpty(answer) && answer.equals(detailDto.getCommitAnswer())) {
                trainDetail.setStatus(CommitAnswerStatusEnums.CORRECT.getIndex());
            }else {
                trainDetail.setStatus(CommitAnswerStatusEnums.ERROR.getIndex());
            }
            trainDetailList.add(trainDetail);
        }
        trainDetailService.saveBatch(trainDetailList);
        //计算正确率
        //正确的题数
        long correct = trainDetailList.stream().filter(detail -> detail.getStatus().equals(CommitAnswerStatusEnums.CORRECT.getIndex())).count();
        BigDecimal correctBig = new BigDecimal(correct);
        BigDecimal countBig = new BigDecimal(trainDetailList.size());
        String accuracy = correctBig.divide(countBig, 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).toString();
        Train train = trainMapper.selectById(trainId);
        train.setAccuracy(accuracy);
        trainMapper.updateById(train);

        //封装返回参数
        List<QuestionResultVo> resultVoList = new ArrayList<>();
        Map<Integer, TrainPaper> questionIdAndPoMap = dbQuesList.stream().collect(Collectors.toMap(TrainPaper::getQuestionId, trainPaper -> trainPaper));
        for (TrainDetail trainDetail : trainDetailList) {
            QuestionResultVo resultVo = new QuestionResultVo();
            TrainPaper trainPaper = questionIdAndPoMap.get(trainDetail.getQuesInfoId());
            BeanUtil.copyProperties(trainDetail, resultVo);
            BeanUtil.copyProperties(trainPaper, resultVo);
            resultVoList.add(resultVo);
        }

        return ResultVo.builder().accuracy(accuracy).questionResultVoList(resultVoList).build();
    }

    @Override
    public Object getList(Integer pageNum, Integer pageSize, Integer stuId) {
        PageHelper.startPage(pageNum == null ? 1:pageNum, pageSize == null ? 10:pageSize);
        List<Train> trains = trainMapper.selectList(Wrappers.<Train>lambdaQuery().eq(Train::getUserId, stuId).orderByDesc(Train::getCreateTime));
        return new PageInfo<>(trains);
    }

    @Override
    public List<QuestionResultVo> getDetail(Integer trainId) {
        List<TrainPaper> dbQuesList = trainPaperService.list(Wrappers.<TrainPaper>lambdaQuery().eq(TrainPaper::getTrainId, trainId));
        List<TrainDetail> trainDetailList = trainDetailService.list(Wrappers.<TrainDetail>lambdaQuery().eq(TrainDetail::getTrainId, trainId));
        Map<Integer, TrainPaper> questionIdAndPoMap = dbQuesList.stream().collect(Collectors.toMap(TrainPaper::getQuestionId, trainPaper -> trainPaper));
        List<QuestionResultVo> resultVoList = new ArrayList<>();
        for (TrainDetail trainDetail : trainDetailList) {
            QuestionResultVo resultVo = new QuestionResultVo();
            TrainPaper trainPaper = questionIdAndPoMap.get(trainDetail.getQuesInfoId());
            BeanUtil.copyProperties(trainDetail, resultVo);
            BeanUtil.copyProperties(trainPaper, resultVo);
            resultVoList.add(resultVo);
        }
        return resultVoList;
    }

    /**
     * 获取该生的前5次考试的错题中涉及的知识点与章节
     * @param stuId
     * @return
     */
    private List<KnowledgeAndChapterVo> getWrongQuestion(Integer stuId) {
        return trainPaperMapper.getWrongQuestion(stuId);
    }

    /**
     * 根据所需的知识点、章节进行组卷
     * @param extractVo
     * @return
     */
    private List<QuestionInfoVo> generateTrainPaper(List<KnowledgeAndChapterVo> extractVo) {
        List<QuestionInfoVo> randomQuestion = new ArrayList<>();
        if (CollUtil.isEmpty(extractVo)) {
            randomQuestion = trainPaperMapper.getRandomQuestion(KnowledgeAndChapterVo.builder().countNum(10).build());
            return randomQuestion;
        }
        //根据条件抽取题目
        for (KnowledgeAndChapterVo vo : extractVo) {
            List<QuestionInfoVo> questionInfoVos = trainPaperMapper.getRandomQuestion(vo);
            randomQuestion.addAll(questionInfoVos);
        }
        //如果抽取的题目数量不足10道，那么就随机抽取题目
        if (randomQuestion.size() < 10) {
            List<QuestionInfoVo> backup = trainPaperMapper.getRandomQuestion(KnowledgeAndChapterVo.builder().countNum(20).build());
            //去重
            List<QuestionInfoVo> finalRandomQuestion = randomQuestion;
            List<QuestionInfoVo> collect = backup.stream().filter(item -> !finalRandomQuestion.contains(item)).collect(Collectors.toList());
            randomQuestion.addAll(collect.subList(0, 10-randomQuestion.size()));
        }
        return randomQuestion;
    }
}
